/*
 * Copyright (c) 2012 Mikaël GUILLEMOT
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.g2de.sound;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Vector;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

/**
 * Celle classe represente le moteur de son, c'est un thread et un singleton. Il
 * assure les fonctions de reglage et de lecture du son et de la musique. Le
 * chargement des sons et des musiques sont effectues par deux classes generee.
 * cela mermet de repertorier les sons et les musique par un identifiant
 * numerique.
 * 
 * @author Mikael GUILLEMOT, Keven Chaussé
 * @version 1.0
 * @since 1.0
 * 
 */
public class SoundEngine extends Thread {
	/**
	 * SCILENCE : represente l'abscence de musique/son
	 */
	public final static short SCILENCE = -1;
	/**
	 * PLAYONCE : pour lire une playlist une seul fois dans l'ordre.
	 */
	public final static short PLAYONCE = 1;
	/**
	 * RANDOM : pour lire une playlist une seul fois dans le desordre.
	 */
	public final static short RANDOM = 2;
	/**
	 * LOOP : si il y a une playlist un boucle dessu, sinon on boucle sur la
	 * musique.
	 */
	public final static short LOOP = 3;
	/**
	 * RANDLOOP : si il y a une playlist un boucle dessu (dans le desordre),
	 * sinon on boucle sur la musique.
	 */
	public final static short RANDLOOP = 4;

	private static SoundEngine instance;

	// TODO passé a des arrayList?
	private Vector<String> soundList;
	private Vector<String> voiceList;

	// TODO parametres statiques a definir : volume, playlist, ...

	/**
	 * methode pour recuperer l'instance de la classe
	 */
	public final synchronized static SoundEngine getInstance() {
		if (instance == null) {
			instance = new SoundEngine();
		}
		return instance;
	}

	/**
	 * constructeur cache (singleton)
	 */
	private SoundEngine() {
		// premiere chose a faire : setVolume avec les paramettres du fichier de
		// config -> les paramettres sont a ajouter au generateur de config.
		// Il faut aussi s'assurer que la biblioteque de son est bien chargée
		// par contre on ne charge pas toutes les musiques !
		soundList = new Vector<String>();
	}

	/**
	 * Add a sound to the list and return its id.
	 * 
	 * @param path
	 */
	public int addSound(String path) {
		soundList.add(path);

		return soundList.size() - 1;
	}

	/**
	 * Add a voice to the list and return its id.
	 * 
	 * @param path
	 */
	public int addVoice(String path) {
		voiceList.add(path);

		return voiceList.size() - 1;
	}

	/**
	 * Permet de regler le volume utilise dans le jeu. Master multiplie tout les
	 * autres parametres, les valeurs attendues vond de 0 a 255.
	 * 
	 * @param master
	 * @param sound
	 * @param voice
	 * @param music
	 */
	public void setVolume(short master, short sound, short voice, short music) {

	}

	/**
	 * Ajoute un son a jouer immediatement, l'appelent ne se soucie pas des
	 * canaux disponibles L'appel est non blocant (thread)
	 * 
	 * @param sound
	 */
	public synchronized void play(int sound) {
		try {
			InputStream in = new FileInputStream(soundList.elementAt(sound));
			Clip clip = AudioSystem.getClip();
			AudioInputStream inputStream = AudioSystem.getAudioInputStream(in);
			clip.open(inputStream);
			clip.start();
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}

	/**
	 * Ajoute un son a jouer immediatement, l'appelent ne se soucie pas des
	 * canaux disponibles L'appel est non blocant (thread)
	 * 
	 * Cette methode a de particulier qu'elle va tenter de situer le son dans
	 * l'espace. (position stereo et atenuation) Pour le moment on gere pas au
	 * delas du 2.1
	 * 
	 * @param sound
	 * @param x
	 * @param y
	 */
	public synchronized void RelativePlay(int sound, int x, int y) {
	}

	/**
	 * Ajoute un son a jouer immediatement, l'appelent ne se soucie pas des
	 * canaux disponibles L'appel est non blocant (thread) si le son se termine
	 * avant time il est rejouer jusqu'a ce que l'on depasse time (en ms).
	 * 
	 * @param sound
	 * @param time
	 */
	public synchronized void play(int sound, int time) {
	}

	/**
	 * Ajoute un son a jouer immediatement, l'appelent ne se soucie pas des
	 * canaux disponibles L'appel est non blocant (thread)
	 * 
	 * @param voice
	 */
	public synchronized void playVoice(int voice) {
		try {
			InputStream in = new FileInputStream(soundList.elementAt(voice));
			Clip clip = AudioSystem.getClip();
			AudioInputStream inputStream = AudioSystem.getAudioInputStream(in);
			clip.open(inputStream);
			clip.start();
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}

	/**
	 * change la musique jouee, l'appel de cette methode annule la playlist en
	 * cours. Pour stopper toute musique il suffi d'utiliser
	 * setMusic(SCILENCE,0,0); soit X le temps de fondu, Y le temps de
	 * transition, M l'instant de fin de la musique en cours: la musique en
	 * cours s'estompe a partir de M-X jusqu'a M. la musique suivante commence a
	 * M+Y-X, et est a pleine puissance a M+Y
	 * 
	 * @param music
	 *            id de la musique a jouer
	 * @param fadeTime
	 *            temps de fondu (atenuation, en ms)
	 * @param transitionTime
	 *            temps de transition en ms
	 */
	public synchronized void setMusic(int music, int fadeTime, int transitionTime) {

	}

	/**
	 * permet de boucler sur la musique, si une playlist existe elle sera prise
	 * en charge meme comportement que setMusic pour les transitions.
	 * 
	 * les types pris en charges sont : PLAYONCE : pour lire une playlist une
	 * seul fois dans l'ordre. RANDOM : pour lire une playlist une seul fois
	 * dans le desordre. LOOP : si il y a une playlist un boucle dessu, sinon on
	 * boucle sur la musique. RANDLOOP : si il y a une playlist un boucle dessu
	 * (dans le desordre), sinon on boucle sur la musique.
	 * 
	 * @param looptype
	 *            le type de bouclage
	 * @param fadeTime
	 *            temps de fondu (atenuation en ms)
	 * @param transitionTime
	 *            temps de transition en ms
	 */
	public synchronized void loopMusic(short looptype, int fadeTime, int transitionTime) {
	}

	/**
	 * indique de travailler sur une playlist pour la musique. Si mon veu
	 * l'annuler il suffi de passer null. ou d'appeler setMusic.
	 * 
	 * @param music
	 */
	public synchronized void setPlaylist(int[] music) {
	}

	@Override
	/**
	 * coeur du thread, ne pas appeler directement
	 */
	public void run() {
		// ici on doit faire la boucle de lecture
	}
}
